using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;
using System.Runtime.InteropServices;
using CourseSync.LibFetcher.Filter;
using System.IO;

namespace CourseSync.LibFetcher.Fetcher
{
	public enum DownloadStatus
	{
		Running,
		Waiting,
		Finished,
		Aborted,
		FileExists,
		WontStart,
		Error,
		HugeFile
	}

	public class Downloader
	{
		private HttpWebResponse _response;

		private DownloadStatus _status = DownloadStatus.WontStart;
		public DownloadStatus Status
		{
			get { return _status; }
		}
		private bool _running = false;
		public bool IsRunning
		{
			get { return _running; }
		}
		private string _message;
		public string Message
		{
			get { return _message; }
		}

		private long _total;
		public long Total
		{
			get { return _total; }

		}
		private long _current;
		public long Current
		{
			get { return _current; }
		}
		private decimal _percentage;
		public decimal Percentage
		{
			get { return _percentage; }
		}



		private Thread _thread;
		public System.Threading.Thread Thread
		{
			get { return _thread; }
		}
		private string _target;
		public string Target
		{
			get { return _target; }
		}
		
		private string _name;
		public string FileName
		{
			get { return _name; }
		}

		private string _courseName;

		public string CourseName
		{
			get { return _courseName; }
		}
		private string _noteName;

		public string NoteName
		{
			get { return _noteName; }
		}


		private string _url;
		public string Url
		{
			get { return _url; }
		}
		private string _method;

		public string Method
		{
			get { return _method; }
		}
		private string _postData;
		public string PostData
		{
			get { return _postData; }
		}

		private int _sizeLimit;
		public int SizeLimit
		{
			get { return _sizeLimit; }
		}
		
		public Downloader(string url, string target_path, string course_name, string note_name, string method, string post_data, int size_limit)
		{
			_noteName = note_name;
			_courseName = course_name;
			_target = target_path;
			_url = url;
			_status = DownloadStatus.Waiting;
			_method=method;
			_postData=post_data;
			_sizeLimit = size_limit;
			_running = false;
		}

		public Downloader(string url, string target_path, string course_name, string note_name)
			: this(url, target_path, course_name, note_name, "GET", "", -1)
		{
			
		}

		public Downloader(string url, string target_path, string course_name, string note_name, int size_limit)
			: this(url, target_path, course_name, note_name, "GET", "",size_limit)
		{

		}

		public void Prepare(string url, HttpWebResponse response, string target_path)
		{
			_status = DownloadStatus.WontStart;
			string fName = "";
			try
			{
				string fileName = response.Headers["Content-Disposition"];
				char[] utf = fileName.ToCharArray();
				byte[] bs = new byte[utf.Length];
				for (int i = 0; i < utf.Length; i++)
					bs[i] = Convert.ToByte(utf[i]);
				fileName = Encoding.GetEncoding("gb2312").GetString(bs);
				BoundFilter bf = new BoundFilter(
					"filename=\"([^\"]*)\""
					);
				fName = bf.GetValue(fileName).Trim();
			}
			catch (System.Exception ex)
			{
				//the name was not resolved
				_status = DownloadStatus.WontStart;
				_message = "File name can not be resolved.";
				if(response!=null)
					response.Close();
				return;
			}

			bool dirReverse=false;
			bool fileReverse=false;
			try
			{
				if(!target_path.EndsWith("\\"))
					target_path+="\\";
				if (File.Exists(target_path + fName))
				{
					_status = DownloadStatus.FileExists;
					_message = "The file exists.";
					response.Close();
					return;
				}
				if (!Directory.Exists(target_path))
				{
					Directory.CreateDirectory(target_path);
					dirReverse=true;
				}				
			}
			catch (System.Exception ex)
			{
				try
				{
					if (dirReverse)
						Directory.Delete(target_path);
				}
				catch
				{
				}
				_status = DownloadStatus.WontStart;
				_message = "Can't create the local file.";
				response.Close();
				return;
			}

			target_path += fName;

			_name = fName;
			_url = url;
			_response = response;
			_target = target_path;
			
			_status = DownloadStatus.Waiting;

		}

		public void Start()
		{
			try
			{
				_thread = new Thread(new ThreadStart(DownloadThread));
				_thread.IsBackground = true;
			}
			catch (System.Exception ex)
			{
				_status = DownloadStatus.WontStart;
				_message = "Can't create the download thread.";
				_response.Close();
				return;
			}
			if (_thread != null && _status==DownloadStatus.Waiting)
				_thread.Start();
		}

		public void Abort()
		{
			if (_thread != null && _status==DownloadStatus.Running)
			{
				_thread.Abort();
				_message = "User abort.";
				_status = DownloadStatus.Aborted;
			}			
		}

		private void DownloadThread()
		{
			if (_running || _status!=DownloadStatus.Waiting)
				return;
			_running = true;
			_status = DownloadStatus.Running;
			string LocalPath = _target;

			try
			{
				using (HttpWebResponse wr = _response)
				{
					using (Stream sIn = wr.GetResponseStream())
					{
						using (FileStream fs = new FileStream(LocalPath, FileMode.CreateNew, FileAccess.Write))
						{
							long length = wr.ContentLength;
							_total = length;
							if (SizeLimit > 0 && length > 1000000 * SizeLimit)
								throw new OverflowException();
							long i = 0;
							int readBytes = 0;
							decimal j = 0;

							while (i < length)
							{
								byte[] buffer = new byte[1024];
								readBytes = sIn.Read(buffer, 0, buffer.Length);
								i += (long)readBytes;
								fs.Write(buffer, 0, readBytes);

								if ((i % 1024) == 0)
								{
									j = Math.Round(Convert.ToDecimal((Convert.ToDouble(i) / Convert.ToDouble(length)) * 100), 4);
									_total = length;
									_current = i;
									_percentage = j;
								}
								else
								{
									_total = length;
									_current = i;
									_percentage = j;
								}
							}
						}
					}
				}
				_current = _total;
				_percentage = 100;
				_status = DownloadStatus.Finished;
			}
			catch (OverflowException)
			{
				if (Target != string.Empty && File.Exists(Target))
					File.Delete(Target);
				_status = DownloadStatus.HugeFile;
				_message = "File too large.";
			}
			catch (IOException)
			{
				if (Target != string.Empty && File.Exists(Target))
					File.Delete(Target);
				_status = DownloadStatus.Error;
				_message = "Can't create the local file.";
			}
			catch (ThreadAbortException)
			{
				if (Target != string.Empty && File.Exists(Target))
					File.Delete(Target);
				_status = DownloadStatus.Aborted;
				_message = "User Abort.";
			}
			catch (Exception ex)
			{
				if (Target != string.Empty && File.Exists(Target))
					File.Delete(Target);
				_status = DownloadStatus.Error;
				_message = "Unknown error.";
			}
			finally
			{
				if (_response != null)
					_response.Close();
				_running = false;
			}
		}
	}
}
